#include "distance.hh"
#include <glm/geometric.hpp>

namespace distance
{
	namespace
	{
		using glm::vec3;

		auto max(float a, float b) { return (a > b ? a : b); }
		auto max(vec3 a, vec3 b) { return vec3{max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)}; }
		auto max(vec3 a, float b) { return max(a, vec3{b}); }

		auto min(float a, float b) { return (a < b ? a : b); }
		auto min(float a, float b, float c) { return min(min(a, b), c); }

		float sphere(vec3 pos, float radius) { return length(pos) - radius; }
		float ground(vec3 pos, float y) { return pos.y - y; }
		float ubox(vec3 pos, vec3 size) { return length(max(abs(pos) - size, 0)); }

		float obj_ground (vec3 pos) { return ground(pos,     -1 ); }
		float obj_jade   (vec3 pos) { return sphere(pos,      1 ); }
		float obj_plastic(vec3 pos) { return   ubox(pos, vec3(1)); }

		float scene(vec3 pos)
		{
			float oz = pos.z;
			vec3 jade_pos = mod(pos - vec3( 1, 0, 0), vec3(4, 2, 4)) - vec3(4, 2, 4)/2.0f;
			vec3 pltc_pos = mod(pos - vec3(-1, 0, 0), vec3(4, 2.2, 4)) - vec3(4, 2.2, 4)/2.0f;
			jade_pos.z = oz;
			pltc_pos.z = oz;
			auto grnd = obj_ground (pos + vec3(0, 1, 0));
			auto jade = obj_jade   (jade_pos);
			auto pltc = obj_plastic(pltc_pos);
			return min(grnd, jade, pltc);
		}
	}

	float estimate(vec3 const& p)
	{
		return scene(p);
	}

	vec3 normal(vec3 const& p)
	{
		constexpr auto small = 1e-4f;

		vec3 result;
		result.x = scene(p + vec3{small, 0.0f, 0.0f}) - scene(p - vec3{small, 0.0f, 0.0f});
		result.y = scene(p + vec3{0.0f, small, 0.0f}) - scene(p - vec3{0.0f, small, 0.0f});
		result.z = scene(p + vec3{0.0f, 0.0f, small}) - scene(p - vec3{0.0f, 0.0f, small});
		return normalize(result);
	}
}

